#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>

int main(int argc, char **argv) {

	if (argc < 2) {
		fprintf(stderr, "Usage: %s [progs]\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	int numeroProgramas = argc - 1;
	int * pipesEscrita[numeroProgramas];
	int estadoExecucaoFilhos[numeroProgramas];
	int pidFilhos[numeroProgramas];

	int i = 0;
	for (i = 0; i < numeroProgramas; i++) {
		int * fdsend = (int *) malloc(sizeof(int) * 2);
		pipe(fdsend);
		pipesEscrita[i] = fdsend;
		estadoExecucaoFilhos[i] = 1;
	}

	for (i = 0; i < numeroProgramas; i++) {
		int pid = fork();
		switch (pid) {
		case -1:
			perror("error");
			exit(EXIT_FAILURE);
		case 0:
			close(pipesEscrita[i][1]);
			dup2(pipesEscrita[i][0], STDIN_FILENO);

			execl(argv[i + 1], argv[i + 1], NULL);
			perror("execl");
			exit(EXIT_FAILURE);
			break;
		default:
			pidFilhos[i] = pid;

			close(pipesEscrita[i][0]);
			break;
		}
	}

	int numeroProgramasTerminados = 0;
	int indiceActual = 0;

	while (numeroProgramasTerminados < numeroProgramas) {
		int status = 0;
		int pidWait = waitpid(pidFilhos[indiceActual], &status, WNOHANG);

		if (pidWait > 0 && (WIFEXITED(status) || WIFSIGNALED(status)))
		// check if alive
				{
			estadoExecucaoFilhos[indiceActual] = 0; // marcar como inactivo
			numeroProgramasTerminados++;

		} else
// ler input e escrever no pipe do filho:
		{
			int size = 250;
			char buffer [size];
			int nBytesRead = read(STDIN_FILENO,buffer,size);
			if (nBytesRead > 0) {
				int nBytes = write(pipesEscrita[indiceActual][1], buffer,
						strlen(buffer));
				if (nBytes != strlen(buffer)) {
					printf("strange: %s %d %d\n", buffer, nBytes,
							strlen(buffer));
				}
				if (nBytes == -1) {
					perror("write");
					exit(EXIT_FAILURE);
				}
			}
		}

		// encontrar o proximo indice
		int flagVolta = 0;
		do {
			indiceActual++;
			if (indiceActual == numeroProgramas) {
				if (flagVolta == 0) {
					indiceActual = 0;
				} else {
					// já tinhamos chegado ao fim da lista uma vez

					if (numeroProgramasTerminados == numeroProgramas) {
						break; // breag agora, e o outer ciclo termina de seguida
					} else {
						// não era suposto termos chegado aqui
						fprintf(stderr, "Erro interno\n");
						exit(EXIT_FAILURE);
					}
				}
			}
		} while (estadoExecucaoFilhos[indiceActual] == 0);

	}
}
